﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sponsorship.Domain;
using Raven.Client;
using Sponsorship.Domain.Persons;
using Sponsorship.Domain.Sponsorships;
using Sponsorship.ReadModel.Sponsors;
using System.Threading;

namespace Sponsorship.ReadModel.Persons
{
    public class DetailsOfPersonHandlers :
        IEventHandler<PersonEntered>,
        IEventHandler<PersonChanged>,
        IEventHandler<SponsorshipEntered>,
        IEventHandler<SponsorshipChanged>,
        IEventHandler<SponsorshipDeleted>
    {
        private readonly IDocumentStore m_documentStore;

        public DetailsOfPersonHandlers(IDocumentStore documentStore)
        {
            m_documentStore = documentStore;
        }


        public void Handle(PersonEntered domainEvent)
        {
            using (var session = m_documentStore.OpenSession())
            {
                var dto = new DetailsOfPersonDto
                {
                    AggregateRootId = domainEvent.PersonId,
                    DateOfBirth = domainEvent.DateOfBirth,
                    FirstName = domainEvent.FirstName,
                    LastName = domainEvent.LastName,
                    TelephoneNumber = domainEvent.TelephoneNumber,

                    Comment = domainEvent.Comment,

                    IsSponsorable = domainEvent.Status== SponsoringStatus.Sponsorable,
                };

                session.Store(dto);

                session.SaveChanges(dto, domainEvent);
            }
        }

        public void Handle(PersonChanged domainEvent)
        {
            using (var session = m_documentStore.OpenSession())
            {
                var dto = session.LoadById<DetailsOfPersonDto>(domainEvent.PersonId);

                dto.Comment = domainEvent.Comment;
                dto.DateOfBirth = domainEvent.DateOfBirth;
                dto.FirstName = domainEvent.FirstName;
                dto.IsSponsorable = domainEvent.Status== SponsoringStatus.Sponsorable;
                dto.LastName = domainEvent.LastName;
                dto.TelephoneNumber = domainEvent.TelephoneNumber;

                session.SaveChanges(dto, domainEvent);
            }
        }

        public void Handle(SponsorshipEntered domainEvent)
        {
            using (var session = m_documentStore.OpenSession())
            {
                var person = session.LoadById<DetailsOfPersonDto>(domainEvent.PersonId);

                var sponsor = GetSponsor(session, domainEvent.SponsorId);

                person.Sponsorships.Add(new DetailsOfPersonDto.Sponsorship()
                {
                    SponsorName = GenerateName(sponsor.FirstName, sponsor.LastName),
                    SponsorshipId = domainEvent.SponsorshipId,
                    SponsorshipTypeId = domainEvent.SponsorshipId,
                    SponsorshipTypeName = "TODO !!!!",// TODO: include type 

                    StartDate = domainEvent.StartDate,
                    EndDate = domainEvent.EndDate,

                    Period = domainEvent.Period.ToString(),

                    Amount = domainEvent.Amount,
                    Comment = domainEvent.Comment,
                });

                session.Store(person);

                session.SaveChanges(person, domainEvent);
            }
        }

        private string GenerateName(string first, string last)
        {
            return first + " " + last;
        }

        private DetailsOfSponsorDto GetSponsor(IDocumentSession session, Guid sponsorId)
        {
            var sponsor = session.LoadById<DetailsOfSponsorDto>(sponsorId);
            if (sponsor == null)
                throw new InvalidOperationException("No Sponsor with given id " + sponsorId + " found");

            return sponsor;
        }

        public void Handle(SponsorshipChanged domainEvent)
        {
            using (var session = m_documentStore.OpenSession())
            {
                var person = session.LoadById<DetailsOfPersonDto>(domainEvent.PersonId);

                var sponsor = GetSponsor(session, domainEvent.SponsorId);

                var sponsorship = person.Sponsorships.Single(x => x.SponsorshipId == domainEvent.SponsorshipId);

                sponsorship.SponsorName = GenerateName(sponsor.FirstName, sponsor.LastName);
                sponsorship.SponsorshipTypeId = sponsorship.SponsorshipTypeId;
                // TODO update sponsorhip type name
                sponsorship.StartDate = domainEvent.StartDate;
                sponsorship.EndDate = domainEvent.EndDate;

                sponsorship.Period = domainEvent.Period.ToString();

                sponsorship.Amount = domainEvent.Amount;
                sponsorship.Comment = domainEvent.Comment;


                session.Store(person);

                session.SaveChanges(person, domainEvent);
            }
        }

        public void Handle(SponsorshipDeleted domainEvent)
        {
            using (var session = m_documentStore.OpenSession())
            {
                var personsWithSponsorShip = session.Query<DetailsOfPersonDto>()
                    .Where(x => x.Sponsorships.Any(sph => sph.SponsorshipId == domainEvent.SponsorshipId))
                    .ToList();

                foreach (var person in personsWithSponsorShip)
                {
                    var sponsorship = person.Sponsorships.Single(x => x.SponsorshipId == domainEvent.SponsorshipId);

                    person.Sponsorships.Remove(sponsorship);

                    session.Store(person);
                }

                session.SaveChanges();
            }
        }
    }
}
